package clock.socoolby.com.clock.state;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.haibin.calendarview.LunarCalendarManager;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import clock.socoolby.com.clock.ClockApplication;
import clock.socoolby.com.clock.Constants;
import clock.socoolby.com.clock.R;
import clock.socoolby.com.clock.alter.AlterManager;
import clock.socoolby.com.clock.event.ClockEvent;
import clock.socoolby.com.clock.event.EventManger;
import clock.socoolby.com.clock.fragment.houranimator.HourAnimatorFactory;
import clock.socoolby.com.clock.model.DateModel;
import clock.socoolby.com.clock.model.SharePerferenceModel;
import clock.socoolby.com.clock.todo.TodoSyncServiceManager;
import clock.socoolby.com.clock.todo.microsoft.bean.todo.TodoEntity;
import clock.socoolby.com.clock.utils.Player;
import clock.socoolby.com.clock.viewmodel.AlterViewModel;
import clock.socoolby.com.clock.viewmodel.DigitViewModel;
import clock.socoolby.com.clock.viewmodel.GlobalViewModel;
import clock.socoolby.com.clock.viewmodel.SimulateViewModel;
import clock.socoolby.com.clock.viewmodel.ThemeUIViewModel;

import static clock.socoolby.com.clock.state.ClockModeEnum.ALTER;
import static clock.socoolby.com.clock.state.ClockModeEnum.HANDUP;
import static clock.socoolby.com.clock.state.ClockTimeTypeEnum.COUNTING;
import static clock.socoolby.com.clock.state.ClockTimeTypeEnum.COUNTING_DOWN;
import static clock.socoolby.com.clock.state.ClockTimeTypeEnum.TIME;

public class ClockStateMachine implements Handler.Callback{

    public static final String TAG=ClockStateMachine.class.getSimpleName();

    GlobalViewModel globalViewModel;
    DigitViewModel digitViewModel;
    SimulateViewModel simulateViewModel;
    ThemeUIViewModel themeUIViewModel;
    AlterViewModel alterViewModel;

    private  Timer timer;
    private Handler handler;
    private final static int UPDATE_TIME = 100;
    private TimerTask timerTask;

    private Date countingDateTimeBase =null;

    private AlterManager alterManager;

    SharePerferenceModel model;

    private boolean heartbeat=false;

    private boolean countingStatePassed=false;

    ClockTimeTypeEnum showTimeType= TIME;

    ClockInterfaceTypeEnum currentInterfaceType;

    TodoSyncServiceManager todoSyncServiceManager;

    TodoEntity currentTodoEntity;

    public ClockStateMachine(AlterManager alterManager,GlobalViewModel globalViewModel, DigitViewModel digitViewModel, SimulateViewModel simulateViewModel,ThemeUIViewModel themeUIViewModel,AlterViewModel alterViewModel,TodoSyncServiceManager todoSyncServiceManager) {
        this.globalViewModel = globalViewModel;
        this.digitViewModel = digitViewModel;
        this.simulateViewModel = simulateViewModel;
        this.alterManager = alterManager;
        this.alterViewModel=alterViewModel;
        this.themeUIViewModel=themeUIViewModel;
        LunarCalendarManager.init(ClockApplication.getContext());
        model=ClockApplication.getInstance().getModel();
        this.todoSyncServiceManager=todoSyncServiceManager;
    }


    public void start(){
        //timber.log.Timber.d("create timer and timerTask.................................");
        timer = new Timer();
        handler = new Handler(this);
        timerTask = new TimerTask() {
            @Override
            public void run() {
                //timber.log.Timber.d("timerTask move...");
                handler.sendEmptyMessage(UPDATE_TIME);
            }
        };
        timer.schedule(timerTask, 1000, 1000);
        if(todoSyncServiceManager.isServiceAble()){
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    todoSyncServiceManager.startSyncAllEntitys();
                }
            }, 15000);
        }
     }

     public void stop(){
        if(timer!=null) {
            timerTask.cancel();
            timer.cancel();
            timer=null;
        }
     }


    @Override
     public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case UPDATE_TIME:
                checkTodoAlter();
                checkHandUp();
                updateTime();
                break;
        }
        return true;
    }

    protected void checkTodoAlter(){
        switch (getCurrentModeEnum()) {
            case ALTER:
            case HANDUP:
                return;
        }

        if(!todoSyncServiceManager.isServiceAble())
            return;

        currentTodoEntity=todoSyncServiceManager.getAlterTodoEntityByDate(new Date());
        if(currentTodoEntity!=null){
            globalViewModel.getClockModeEnum().setValue(ALTER);
            EventManger.post(ClockEvent.buildAlterTodo(true));
        }
    }

    public void setCurrentShowTimeType(ClockTimeTypeEnum type){
        switch (getCurrentModeEnum()){
            case ALTER:
            case HANDUP:
                return;
            case DELAY:
                stopHandUpDelay();
                countingDateTimeBase=null;
                return;
            case NORMAL:
                if(countingStatePassed&&(showTimeType==COUNTING||showTimeType==COUNTING_DOWN)){
                    EventManger.post(ClockEvent.buildCountingStateChanged(false));
                }
                break;
        }

        this.showTimeType=type;
        countingDateTimeBase=null;
        countingStatePassed=false;
        switch (type){
            case COUNTING:
                countingDateTimeBase =new Date();
                break;
            case COUNTING_DOWN:
                break;
        }
        updateDescription(model.getDescription());
    }

    public ClockTimeTypeEnum getCurrentShowTimeType(){
        return showTimeType;
    }


    private ClockModeEnum getCurrentModeEnum(){
        return globalViewModel.getClockModeEnum().getValue();
    }

    private ClockInterfaceTypeEnum getCurrentInterfaceType(){
        return currentInterfaceType;
    }

    private boolean isDigitClockInterface(){
        return currentInterfaceType==ClockInterfaceTypeEnum.Digit;
    }

    public void setCurrentInterfaceType(ClockInterfaceTypeEnum currentInterfaceType){
        this.currentInterfaceType=currentInterfaceType;
        if(countingStatePassed&&currentInterfaceType==ClockInterfaceTypeEnum.Digit){
            EventManger.post(ClockEvent.buildCountingStateChanged(true));
        }
    }

    String timeString;
    DateModel date;
    private void updateTime() {
        heartbeat=!heartbeat;
        date= new DateModel();
        EventManger.post(ClockEvent.buildHeartbeat(heartbeat));
        switch (getCurrentModeEnum()){
            case NORMAL:
                switch (showTimeType){
                    case COUNTING:
                        if(!countingStatePassed) {
                            if(isDigitClockInterface()) {
                                DateModel temp = new DateModel(countingDateTimeBase);
                                digitViewModel.setTimeText(temp.getTimeString(false));
                            }
                            if (!model.isTickSound()) {
                                Player.getInstance().playTick(ClockApplication.getContext(), R.raw.tick2);
                            }
                        }else{//暂停时开始时间自动后延
                            countingDateTimeBase.setTime(countingDateTimeBase.getTime()+1000);
                        }
                        break;
                    case COUNTING_DOWN:
                        if(!countingStatePassed&&isDigitClockInterface()) {
                            digitViewModel.setTimeText(DateModel.getTimeFull(globalViewModel.getHandUpTime().getValue()));
                        }
                        break;
                    default:
                        if(isDigitClockInterface()) {
                            timeString = model.isDisplaySecond() ? date.getTimeString(model.isHourSystem12()) : date.getShortTimeString(heartbeat, model.isHourSystem12());
                            digitViewModel.setTimeText(timeString);
                        }
                        break;
                }
                break;
            case HANDUP:
            case ALTER:
                break;
            case DELAY:
                if(countingDateTimeBase==null)
                    countingDateTimeBase=new Date();
                if(isDigitClockInterface()) {
                    DateModel temp = new DateModel(countingDateTimeBase);
                    digitViewModel.setTimeText(temp.getTimeString(false));
                }
                if (!model.isTickSound()) {
                    Player.getInstance().playTick(ClockApplication.getContext(), R.raw.tick2);
                }
                break;
        }
        if(getCurrentModeEnum()!=HANDUP&&getCurrentModeEnum()!=ALTER)
           reportTime(date);
        updateHourSytemStyle(date);
        updateDay(date);
    }


    int beforeHour;
    private void updateHourSytemStyle(DateModel date){
        beforeHour=globalViewModel.getTime_hour().getValue();
        if(beforeHour!=date.getHour()) {
            globalViewModel.setTime_hour(date.getHour());
            timber.log.Timber.d("in state machine hour changed:"+beforeHour+"->"+date.getHour());
            if(isHourAnimatorAble()&&date.getMinute()==0) {
                if (HourAnimatorFactory.isHourAnimator(globalViewModel.getHourAlterTypeName().getValue()))
                    EventManger.post(ClockEvent.buildHourAnimator(true));
            }else{
                //少了判断，增加随机性
                int sleepHours=Math.abs(date.getHour()-beforeHour);
                if(sleepHours>=2)
                    EventManger.post(ClockEvent.buildLongSleepWakeUp(sleepHours));
            }

            if(todoSyncServiceManager.isServiceAble()) {
                todoSyncServiceManager.startSyncAllEntitys();
            }
        }
    }

    private boolean isHourAnimatorAble(){
       boolean ret=getCurrentModeEnum()==ClockModeEnum.NORMAL;
       return ret&&getCurrentShowTimeType()==TIME;
    }

    DateModel currentDate=null;
    private void updateDay(DateModel date){
        if(currentDate==null||currentDate.getDay()!=date.getDay()) {
            timber.log.Timber.d("updateDay."+date);
            currentDate=date;
            String dayString = date.getToday();
            String dateString = date.getDateString();
            String calendarFestival=getCalendarFestival(date);
            if(calendarFestival!=null&&!calendarFestival.isEmpty())
                dayString=calendarFestival+"    "+dayString;
            themeUIViewModel.getDayDescription().setValue(dayString);
            themeUIViewModel.getWeekDescription().setValue(dateString);
            EventManger.post(ClockEvent.buildNewDay(currentDate));
        }
    }

    private String getCalendarFestival(DateModel dateModel){
        String festival=LunarCalendarManager.gregorianFestival(dateModel.getMonth(),dateModel.getDay());
        String ret=LunarCalendarManager.getTraditionFestival(dateModel.getYear(),dateModel.getMonth(),dateModel.getDay());
        if(ret==null&&festival==null)
            return null;
        if(ret==null||ret.isEmpty())
            return festival;
        if(festival==null||festival.isEmpty())
            return ret;
        return festival+"  "+ret;
    }


    private void reportTime(DateModel date) {
        if (globalViewModel.isTickSound()) {
            Player.getInstance().playTick(ClockApplication.getContext(),R.raw.tick2);
        }
        int year = date.getYear();
        int month = date.getMonth();
        int day = date.getDay();
        int hour = date.getHour();
        int minute = date.getMinute();
        int second = date.getSecond();
        int today = date.getWeek();

        if (model.getTypeHourPower() != Constants.TALKING_NO_REPORT) {
            //timber.log.Timber.d(String.format("reportTime Year:%d Month:%d Day:%d Hour:%d Minute:%d Today:%d", year, month, day, hour, minute, today));
            if (!alterManager.isReport(hour, minute))
                return;
            if ((minute == 30 || minute == 0) && model.getTypeHourPower() == Constants.TALKING_HALF_AN_HOUR && second == 0) {
                Player.getInstance().reportTime(ClockApplication.getContext(), year, month, day, hour, minute, today);
            } else if (model.getTypeHourPower() == Constants.TALKING_HOURS && minute == 0 && second == 0) {
                Player.getInstance().reportTime(ClockApplication.getContext(), year, month, day, hour, minute, today);
            }
        }
    }

    public void destory() {
        if(timer!=null)
           timer.cancel();
    }

    //alter
    public void stopAlter(Date newAlterDate){
        EventManger.post(ClockEvent.buildAlterTodo(false));
        if(newAlterDate!=null){
            currentTodoEntity.getReminderdatetime().setDateTime(newAlterDate);
            todoSyncServiceManager.delayTodoEntity(currentTodoEntity);
        }else
            todoSyncServiceManager.completeTodoEntity(currentTodoEntity);
        globalViewModel.getClockModeEnum().setValue(ClockModeEnum.NORMAL);
    }

    //handUp
    public void stopHandUp(boolean userCheckStop){
        EventManger.post(ClockEvent.buildHandup(false));
        if(!userCheckStop&&model.isHandUpOverTimeCountingAble()&&showTimeType==TIME){
            globalViewModel.getClockModeEnum().setValue(ClockModeEnum.DELAY);
        }else
            globalViewModel.getClockModeEnum().setValue(ClockModeEnum.NORMAL);
    }

    protected void stopHandUpDelay(){
        globalViewModel.getClockModeEnum().setValue(ClockModeEnum.NORMAL);
    }

    public void handUpCountingDownCheck(){
        if(isHandUpAbla()){
            if(showTimeType==ClockTimeTypeEnum.COUNTING_DOWN)
                setCurrentShowTimeType(TIME);
            else
                setCurrentShowTimeType(ClockTimeTypeEnum.COUNTING_DOWN);
        }
    }

    public void countingCheck(){
        if(showTimeType!= ClockTimeTypeEnum.COUNTING){
            setCurrentShowTimeType(ClockTimeTypeEnum.COUNTING);
        }else{
            setCurrentShowTimeType(ClockTimeTypeEnum.TIME);
        }
    }

    protected boolean isHandUpAbla(){
        return getCurrentModeEnum()!=ALTER&&globalViewModel.getHandUpAble().getValue();
    }

    protected void checkHandUp(){
        if(!isHandUpAbla()) {
            return;
        }
        int handUpTime=globalViewModel.getHandUpTime().getValue();
        switch (getCurrentModeEnum()) {
            case NORMAL:
                if(countingStatePassed&&getCurrentShowTimeType()== COUNTING_DOWN)
                    break;
                globalViewModel.getHandUpTime().setValue(handUpTime-1);
                if (handUpTime <= 10&&handUpTime > 0)
                    updateDescription("提醒时间倒计时： " + globalViewModel.getHandUpTime().getValue());
                if (handUpTime == 0) {
                    globalViewModel.getHandUpTime().setValue(model.getHandUpTime());
                    globalViewModel.getClockModeEnum().setValue(HANDUP);
                    updateDescription(model.getDescription());
                    EventManger.post(ClockEvent.buildHandup(true));
                }
                break;
            case HANDUP:
                break;
            case DELAY:
                if(countingDateTimeBase==null)
                    countingDateTimeBase =new Date();
                break;
        }
    }

    protected void updateDescription(String desc){
        themeUIViewModel.setDescription(desc);
    }

    public void uiClockChecked() {
        switch (getCurrentModeEnum()){
            case NORMAL:
                switch (getCurrentShowTimeType()){
                    case TIME:
                        break;
                    case COUNTING_DOWN:
                    case COUNTING:
                        this.countingStatePassed=!countingStatePassed;
                        EventManger.post(ClockEvent.buildCountingStateChanged(countingStatePassed));
                        break;
                }
                break;
            case HANDUP:
            case ALTER:
                break;
            case DELAY:
                setCurrentShowTimeType(TIME);
                break;
        }
    }

    public TodoEntity getCurrentTodoEntity() {
        return currentTodoEntity;
    }
}
